#include <linux/init.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#include <linux/mm.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/fs.h>

char * vm_area = NULL;

ssize_t mmap_drv_read(struct file * filp, char __user * buff, size_t size, loff_t * pos)
{
	int ret;
	ret = copy_to_user(buff, vm_area, size);
	if(ret > 0)
	{
		printk("copy_to_user error\n");
		return -EAGAIN;
	}
	return size;
}

int mmap_drv_mmap(struct file * filp, struct vm_area_struct * vm)
{
	int ret;
	unsigned long phy_area = virt_to_phys(vm_area);
	//实现用户空间虚拟地址，和内核空间物理地址映射
	//参数1：虚拟地址空间的结构体
	//参数2：虚拟地址的起始
	//参数3：物理地址的页帧号
	//参数4：映射的大小
	//参数5：映射空间的权限
	ret = remap_pfn_range(vm, vm->vm_start, (phy_area >> PAGE_SHIFT), PAGE_SIZE, vm->vm_page_prot);
	if(ret < 0)
	{
		printk("remap_pfn_range error\n");
		return -EINVAL;
	}
	return 0;
}

const struct file_operations mmap_fops = {
	.read = mmap_drv_read,
	.mmap = mmap_drv_mmap,
};

struct miscdevice mmap_misc = {
	.minor = 199,
	.name = "mmap_drv",
	.fops = &mmap_fops,
};

static int __init mmap_drv_init(void)
{
	//1. 注册杂项设备
	int ret;
	ret = misc_register(&mmap_misc);
	if(ret < 0)
	{
		printk("misc_register error\n");
		return -EINVAL;
	}

	//2. 在内核空间申请一片虚拟地址(等会转化成物理地址)
	vm_area = kzalloc(PAGE_SIZE, GFP_KERNEL);
	if(vm_area == NULL)
	{
		printk("kzalloc error\n");
		misc_deregister(&mmap_misc);
		return -ENOMEM;
	}
	return 0;
}

static void __exit mmap_drv_exit(void)
{
	kfree(vm_area);
	misc_deregister(&mmap_misc);
}

module_init(mmap_drv_init);
module_exit(mmap_drv_exit);
MODULE_LICENSE("GPL");

